﻿using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using System.Reflection;
using System.Text;
using YiShop.Framework.Authorization;
using YiShop.Framework.Cache;
using YiShop.Framework.Config;
using YiShop.Framework.EventBus;
using YiShop.Framework.SqlSugar;

namespace YiShop.Framework
{
    public static class AppServiceCollectionExtensions
    {
        public static IServiceCollection AddInject(this IServiceCollection services, IConfiguration configuration)
        {
            AppSettings.Init(configuration);

            services.AddCache(configuration).AddCaptcha();
            services.AddScoped<ISqlSugarClient>(s =>
            {
                return new SqlSugarClient(new ConnectionConfig()
                {
                    DbType = DbType.MySql,
                    ConnectionString = configuration["ConnectionConfig:ConnectionString"],
                    IsAutoCloseConnection = true,
                });
            });
            services.AddScoped(typeof(ISqlSugarRepository<>), typeof(SqlSugarRepository<>));

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "YiShip API", Version = "v1" });
            });

            var key = AppSettings.JWTSettings.IssuerSigningKey;
            services.AddAuthentication(config =>
            {
                config.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
            })
            .AddJwtBearer(config =>
            {
                config.RequireHttpsMetadata = false;
                config.SaveToken = true;
                config.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(key)),
                    ValidateIssuer = false,
                    ValidateAudience = false
                };
            });
            services.AddAppAuthorization<PermissionHandler>();

            services.AddCors(options =>
            {
                options.AddDefaultPolicy(
                    policy =>
                    {
                        policy.WithOrigins("http://localhost").AllowAnyHeader().AllowAnyMethod().AllowCredentials();
                    });
            });

            services.AddHttpContextAccessor();

            services.AddStartups();

            return services;
        }

        /// <summary>
        /// 添加 Startup 自动扫描
        /// </summary>
        /// <param name="services">服务集合</param>
        /// <returns>服务集合</returns>
        private static IServiceCollection AddStartups(this IServiceCollection services)
        {
            var mvcBuilder = services.AddControllers(opt =>
            {
                opt.Filters.Add(typeof(GlobalExceptionFilter));
            }).ConfigureApiBehaviorOptions(options =>
            {
                options.InvalidModelStateResponseFactory = context =>
                {
                    //获取验证失败的模型字段 
                    var errors = context.ModelState
                        .Where(s => s.Value != null && s.Value.ValidationState == ModelValidationState.Invalid)
                        .SelectMany(s => s.Value!.Errors.ToList())
                        .Select(e => e.ErrorMessage);
                    var strerr = string.Join('、', errors);
                    // 统一返回格式
                    var result = AjaxResult.Error(strerr);
                    return new BadRequestObjectResult(result);
                };
            });

            var assemblys = AppDomain.CurrentDomain.GetAssemblies().Where(ass => ass.FullName!.StartsWith(nameof(YiShop))); //获取所有加载的程序集

            services.AddTransient<IDomainEventDispatcher, MediatrDomainEventDispatcher>();
            services.AddMediatR(cfg => cfg.RegisterServicesFromAssemblies(assemblys.ToArray()));

            foreach (var assembly in assemblys)
            {
                mvcBuilder.AddApplicationPart(assembly);

                //加载所有AppStartup中的ConfigureServices
                var allTypes = assembly.GetTypes();
                var startupTypes = allTypes.Where(t => typeof(AppStartup).IsAssignableFrom(t) && !t.IsAbstract).FirstOrDefault();
                if (startupTypes != null && Activator.CreateInstance(startupTypes) is AppStartup appStartup)
                {
                    MethodInfo? method = startupTypes.GetMethod("ConfigureServices");
                    method?.Invoke(appStartup, new[] { services });
                }

                var iTransientServices = allTypes.Where(t => typeof(ITransient).IsAssignableFrom(t) && t.IsClass && !t.IsAbstract);
                foreach (var iTransientService in iTransientServices)
                {
                    services.AddTransient(iTransientService);
                }

            }

            return services;
        }

    }
}
